import json
import pandas as pd
from typing import List, Dict, Tuple
from langchain_community.embeddings import HuggingFaceEmbeddings  # 导入HuggingFace的文本嵌入模型
from langchain_community.vectorstores import Chroma  # 导入向量数据库
from langchain.text_splitter import CharacterTextSplitter  # 文本分割工具
import os
from dotenv import load_dotenv  # 环境变量加载工具

# 简历匹配器类：用于实现简历与职位的智能匹配
class ResumeJobMatcher:
    def __init__(self, job_data_path: str, resume_data_path: str):
        """
        初始化简历匹配器
        :param job_data_path: Excel文件路径，包含企业岗位信息，预期包含列：公司名称、职位名称、职位要求、薪资范围
        :param resume_data_path: JSON文件路径，包含简历信息，包括基本信息、专业技能、工作经历等
        """
        self.job_data = pd.read_excel(job_data_path)  # 读取职位数据Excel文件
        with open(resume_data_path, 'r', encoding='utf-8') as f:
            self.resume_data = json.load(f)  # 读取简历JSON数据
        
        # 初始化文本嵌入模型，用于将文本转换为向量
        self.embeddings = HuggingFaceEmbeddings(
            model_name="BAAI/bge-large-zh-v1.5",  # 使用中文预训练模型
            model_kwargs={'device': 'gpu'}  # 使用GPU加速处理
        )
        self.job_vectorstore = self._create_job_vectorstore()  # 创建职位向量数据库

    def _create_job_vectorstore(self) -> Chroma:
        """
        创建职位信息的向量数据库
        将所有职位信息转换为向量形式，用于后续的相似度匹配
        :return: 返回创建好的向量数据库
        """
        # 准备数据结构
        job_texts = []  # 存储所有职位描述文本
        metadatas = []  # 存储职位的元数据信息
        
        # 遍历每个职位记录，转换为文本格式
        for _, row in self.job_data.iterrows():
            job_text = self._format_job_text(row)  # 将职位信息格式化为文本
            job_texts.append(job_text)
            
            # 保存职位的关键信息作为元数据
            metadatas.append({
                'company': row['单位名称'],  # 公司名称
                'position': row['岗位名称'],  # 职位名称
                'salary': row['最低薪资（K/月）'],  # 薪资范围
                'requirements': row['岗位职责'+'岗位要求']  # 职位要求
            })

        # 创建向量数据库
        vectorstore = Chroma.from_texts(
            texts=job_texts,  # 职位描述文本列表
            embedding=self.embeddings,  # 使用的嵌入模型
            metadatas=metadatas,  # 职位元数据
            collection_name="jobs"  # 集合名称
        )
        return vectorstore

    def _format_job_text(self, job_row: pd.Series) -> str:
        """
        将职位信息格式化为文本字符串，用于向量化处理
        :param job_row: 包含职位信息的DataFrame行
        :return: 格式化后的文本字符串
        """
        return f"""
        公司：{job_row['单位名称']}
        职位：{job_row['岗位名称']}
        要求：{job_row['岗位职责'+'岗位要求']}
        薪资：{job_row['最低薪资（K/月）']}
        """

    def _format_resume(self, resume: Dict) -> str:
        """
        将简历信息格式化为文本字符串，用于与职位进行匹配
        :param resume: 简历信息字典
        :return: 格式化后的文本字符串
        """
        text = f"""
        专业技能：{resume['专业技能']}
        工作经历：{resume['工作经历']}
        项目经历：{resume['项目经历']}
        教育经历：{resume['教育经历']['专业']} {resume['教育经历']['学历']}
        期望职位：{resume['意向岗位']['职位']}
        期望行业：{resume['意向岗位']['行业']}
        期望薪资：{resume['意向岗位']['薪资']}
        """
        return text

    def match_jobs(self, resume_index: int, top_k: int = 10) -> Tuple[List[Dict], str]:
        """
        为指定简历匹配最适合的职位
        :param resume_index: 要匹配的简历在简历列表中的索引
        :param top_k: 需要返回的最佳匹配职位数量
        :return: 返回一个元组：(匹配到的职位列表, 简历文本)
                匹配的职位列表中每个职位包含：公司名称、职位名称、职位要求、薪资范围、匹配度
        """
        # 获取并处理简历信息
        resume = self.resume_data[resume_index]  # 获取指定简历
        resume_text = self._format_resume(resume)  # 格式化简历文本
        
        # 使用向量数据库进行相似度搜索
        results = self.job_vectorstore.similarity_search_with_scores(
            resume_text,  # 用于搜索的简历文本
            k=top_k  # 返回前k个最相似的结果
        )
        
        # 处理匹配结果
        matched_jobs = []
        for doc, score in results:
            # 计算匹配度（将向量距离转换为百分比）
            match_percentage = (1 - score) * 100
            
            # 获取职位信息
            metadata = doc.metadata
            
            # 构建匹配结果字典
            job_match = {
                'company': metadata['company'],  # 公司名称
                'position': metadata['position'],  # 职位名称
                'requirements': metadata['requirements'],  # 职位要求
                'salary': metadata['salary'],  # 薪资范围
                'match_percentage': round(match_percentage, 2)  # 匹配度（保留2位小数）
            }
            matched_jobs.append(job_match)  # 将匹配结果添加到列表
        
        return matched_jobs, resume_text  # 返回匹配结果和简历文本 